﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MoneyWentWhere.DataModel;
using System.IO;
using MoneyWentWhere.WinFormsUI.Properties;
using MoneyWentWhere.DataModel.Storage;
using System.Threading;
using System.Diagnostics;
using MoneyWentWhere.WinFormsUI.UI;
using MoneyWentWhere.DataModel.Reports;

namespace MoneyWentWhere.WinFormsUI
{
    public partial class MainForm : Form
    {
        readonly FileManager FileManager = new FileManager();


        MoneyDataSet Document { get { return FileManager.Document; } }

        public MainForm()
        {
            InitializeComponent();
            FileManager.PropertyChanged += FileManager_PropertyChanged;

            this.Left = 0;
            this.Width = Screen.FromControl(this).WorkingArea.Width;
        }

        void FileManager_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // Bind data sources
            if (e.PropertyName == "Document")
            {
                accountListBS.DataSource = Document.Accounts;
                accountPage.Account = lbAccts.SelectedItem as Account;
            }

            // UI settings
            autoSaveToolStripMenuItem.Checked = Settings.Default.AutoSave;
            openOnStartupToolStripMenuItem.Checked = FileManager.OpenOnStartup;

            // Title
            String filename = Path.GetFileName(FileManager.Filename ?? "untitled");
            Text = String.Format(Resources.AppTitle, filename);
        }

        void MainForm_Shown(object sender, EventArgs e)
        {
            FileManager_PropertyChanged(this, new PropertyChangedEventArgs("Document"));
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            switch (PromptToSave())
            {
                case DialogResult.Cancel: e.Cancel = true; return; 
                case DialogResult.Yes: Save(); break;
            }

            Settings.Default.Save();
        }

        /// <summary>
        /// Prompt if a file should be saved (yes/no/cancel)
        /// </summary>
        /// <returns></returns>
        DialogResult PromptToSave()
        {
            if (AutoSave && FileManager.Filename != null) { return DialogResult.Yes; }

            String p = String.Format(Resources.saveChangesPrompt, Document.StorageInfo.Filename);
            var dr = MessageBox.Show(this, p, Text, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
            return dr;
        }

        /// <summary>
        /// Save the file, optionally prompt for confirmation and filename.
        /// </summary>
        void Save(bool askFileName = false)
        {
            // Prompt for filename if unknown
            if (askFileName || FileManager.Filename == null) 
            {
                var dr = saveFileDialog.ShowDialog();
                if (dr != DialogResult.OK) { return; }

                FileManager.Filename = saveFileDialog.FileName;
            }

            // BUG: If we add a new row without starting to edit, it doesn't get saved
            if (accountPage.dgTransactions.IsCurrentCellInEditMode)
            {
                accountPage.dgTransactions.CommitEdit(DataGridViewDataErrorContexts.Commit);
            }

            FileManager.TrySaveDocument();
        }

        #region File Menu

        bool AutoSave
        {
            get { return autoSaveToolStripMenuItem.Checked; }
            set { autoSaveToolStripMenuItem.Checked = value; }
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // TODO: dirty flag support
            switch (PromptToSave())
            {
                case DialogResult.Cancel: return;
                case DialogResult.Yes: Save(); break;
            }

            // Create and save
            FileManager.Document = new MoneyDataSet();
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // TODO: dirty flag support
            switch (PromptToSave())
            {
                case DialogResult.Cancel: return;
                case DialogResult.Yes: Save(); break;
            }

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                FileManager.TryOpenDocument(openFileDialog.FileName);
            }
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Save();
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Save(askFileName: true);
        }

        private void openOnStartupToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
        {
            FileManager.OpenOnStartup = openOnStartupToolStripMenuItem.Checked;
        }

        private void autoSaveToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
        {
            Settings.Default.AutoSave = autoSaveToolStripMenuItem.Checked;
            Settings.Default.Save();
        }

        #endregion

        private void passwordToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetPasswordDialog d = new SetPasswordDialog();
            d.FileEncrypted = !String.IsNullOrEmpty(FileManager.Password);

            if (d.ShowDialog(this) == DialogResult.OK)
            {
                if (d.FileEncrypted)
                {
                    // Set or change password
                    FileManager.Password = d.Password;
                }
                else
                {
                    // Set or change password
                    FileManager.Password = null;
                }
            }
        }

        private void dgTransactions_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            Trace.WriteLine("Error in row: " + e.RowIndex + " col: " + e.ColumnIndex); 
        }

        private void tbAddAcct_Click(object sender, EventArgs e)
        {
            int num = Document.Accounts.Count + 1;
            Account ac = Document.CreateAccount("Acct #" + num);

            lbAccts.SelectedItem = ac;

            // BUGFIX: for the first account, page is not 
            // refreshed through SelectedIndexChanged
            if (lbAccts.SelectedIndex == 0) { accountPage.Account = ac; }

            accountPage.tbAcctName.Focus();
            accountPage.tbAcctName.SelectAll();
        }

        private void tbAcctDelete_Click(object sender, EventArgs e)
        {
            Account ac = lbAccts.SelectedItem as Account;
            if (ac == null) { return; }

            bool acIsEmpty = ac.Transactions.All(x => x.Amount == 0);

            if (acIsEmpty ||
                MessageBox.Show(this, 
                    String.Format(Resources.deleteAcctPrompt, ac.Name), Resources.deleteAcctTitle,
                    MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
            {
                ac.Delete();

                // BUGFIX: for the first account, page is not 
                // refreshed through SelectedIndexChanged
                if (lbAccts.SelectedIndex == 0) { accountPage.Account = lbAccts.SelectedItem as Account; }
                if (lbAccts.SelectedIndex == -1) { accountPage.Account = null; }
            }
        }

        private void lbAccts_SelectedIndexChanged(object sender, EventArgs e)
        {
            // BUGFIX: null SelectedItem occurs on *any* property change
            // (framework refreshes the item). We *must* ignore this.
            if (lbAccts.SelectedItem == null) { return; }

            accountPage.Account = lbAccts.SelectedItem as Account;
        }

        private void tbAcctUp_Click(object sender, EventArgs e)
        {
            Account ac = lbAccts.SelectedItem as Account;
            if (ac == null) { return; }

            int index = Document.Accounts.IndexOf(ac);
            if (index == 0) { return; }
            Document.Accounts.Move(index, index - 1);
            lbAccts.SelectedItem = ac;
        }

        private void tbAcctDown_Click(object sender, EventArgs e)
        {
            Account ac = lbAccts.SelectedItem as Account;
            if (ac == null) { return; }

            int index = Document.Accounts.IndexOf(ac);
            if (index >= Document.Accounts.Count - 1) { return; }
            Document.Accounts.Move(index, index + 1);
            lbAccts.SelectedItem = ac;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void bnGoTo_Click(object sender, EventArgs e)
        {
            String text = Clipboard.GetText(TextDataFormat.Text);
            if (text != null && text.Length > 20) { text = null; }

            String target = InputBox.GetText("Go to Transaction",
                "Enter guid or accountName:index for the transaction", text);

            if (target == null) { return; }

            Transaction tx = ParseTxReference(target);
            if (tx == null)
            {
                MessageBox.Show("Not found: " + target);
                return;
            }

            SelectTransaction(tx);
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (keyData == (Keys.Control | Keys.G))
            {
                bnGoTo_Click(this, EventArgs.Empty);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Select the account and the transaction in the main window.
        /// </summary>
        /// <param name="tx"></param>
        void SelectTransaction(Transaction tx)
        {
            int acIndex = Document.Accounts.IndexOf(tx.Account);
            lbAccts.SelectedIndex = acIndex;

            // Go to the description cell for the tx
            int txIndex = tx.Account.Transactions.IndexOf(tx);
            accountPage.dgTransactions.CurrentCell = accountPage.dgTransactions.Rows[txIndex].Cells[3];
        }

        Transaction ParseTxReference(String text)
        {
            if (text == null) { return null; }

            // Find by Guid
            Guid txGuid;
            if (Guid.TryParse(text, out txGuid))
            {
                object obj = null;
                Document.ObjectByGuid.TryGetValue(txGuid, out obj);
                Transaction tx = obj as Transaction;
                if (tx != null) { return tx; }
                return null;
            }

            // Find by acctName:index
            int pos = text.LastIndexOf(':');
            if (pos == -1) { pos = text.LastIndexOf(' '); }

            if (pos != -1)
            {
                String acctS = text.Substring(0, pos);

                // 1. Find account
                // try exact match
                Account acct = Document.Accounts
                    .FirstOrDefault(x => x.Name.Equals(acctS, StringComparison.CurrentCultureIgnoreCase));
                if (acct == null)
                {
                    // Try starts with
                    acct = Document.Accounts
                        .FirstOrDefault(x => x.Name.StartsWith(acctS, StringComparison.CurrentCultureIgnoreCase));
                }
                if (acct == null) { return null; }

                // 2. Find transaction by index
                String txIndexS = text.Substring(pos + 1);
                int txIndex = -1;
                if (int.TryParse(txIndexS, out txIndex))
                {
                    if (txIndex < 0 || txIndex >= acct.Transactions.Count) { return null; }
                    return acct.Transactions[txIndex];
                }
            }

            return null;
        }

    }
}
